package com.study.reactor.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.sd.bsf.web.util.ReqCtxUtil;
import com.sd.ims.collection.common.constant.CommonCons;
import com.sd.ims.collection.common.enums.*;
import com.sd.ims.collection.common.exception.BusinessException;
import com.sd.ims.collection.common.exception.ImsException;
import com.sd.ims.collection.common.utils.BigDecimalUtil;
import com.sd.ims.collection.common.utils.SerialNo;
import com.sd.ims.collection.core.biz.arreceive.AbstractArWriteOff;
import com.sd.ims.collection.core.biz.arreceive.bean.OldDataWriteOffJobParam;
import com.sd.ims.collection.core.biz.arreceive.bean.SelectReceiptParamDTO;
import com.sd.ims.collection.core.biz.arreceive.bean.SelectRefundParamDTO;
import com.sd.ims.collection.core.biz.arreceive.bean.SelectReturnParamDTO;
import com.sd.ims.collection.core.biz.arreceive.document.ReceiptDocumentServer;
import com.sd.ims.collection.core.biz.arreceive.document.RefundDocumentServer;
import com.sd.ims.collection.core.biz.arreceive.document.ReturnDocumentServer;
import com.sd.ims.collection.core.biz.arreceive.request.ArWriteOffInsertRequest;
import com.sd.ims.collection.core.biz.receive.ReceiveDocumentBiz;
import com.sd.ims.collection.core.dao.bo.ReceiveDocumentBO;
import com.sd.ims.collection.core.dao.bo.ReceiveRefundBO;
import com.sd.ims.collection.core.dao.bo.ReceiveReturnBO;
import com.sd.ims.collection.core.dao.mapper.ReceiveRefundDetailMapper;
import com.sd.ims.collection.core.dao.mapper.ReceiveRefundMapper;
import com.sd.ims.collection.core.dao.po.*;
import com.sd.ims.collection.core.service.ArReceiveReturnService;
import com.sd.ims.collection.core.service.ReceiveDocumentService;
import com.sd.ims.collection.core.service.ReceiveRefundServer;
import com.sd.ims.collection.core.service.impl.ArWriteOffDetailServer;
import com.sd.ims.collection.core.service.impl.ArWriteOffServer;
import com.study.reactor.dao.bo.ReceiveDocumentBO;
import com.study.reactor.dao.bo.ReceiveRefundBO;
import com.study.reactor.dto.SelectRefundParamDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author Cliff
 * Created on 2022/8/25
 * desc
 */
@Service
@Slf4j
public class RefundDocumentServerImpl extends AbstractArWriteOff implements RefundDocumentServer {


    @Autowired
    private ReceiveRefundDetailMapper receiveRefundDetailMapper;
    @Autowired
    private ReceiveRefundMapper receiveRefundMapper;
    @Autowired
    private ReceiveDocumentBiz receiveDocumentBiz;
    @Autowired
    private ReceiveDocumentService receiveDocumentService;
    @Autowired
    private ArWriteOffServer arWriteOffServer;
    @Autowired
    private ArWriteOffDetailServer arWriteOffDetailServer;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private ReceiveRefundServer receiveRefundServer;
    @Autowired
    private ArReceiveReturnService arReceiveReturnService;

    @Autowired
    private ReceiptDocumentServer receiptDocumentServer;
    @Autowired
    private ReturnDocumentServer returnDocumentServer;

    /**
     * 退款单与收款单的核销
     *
     * 1. 根据客户编码查询退款单，
     * 2. 查询收款单，根据客户编码进行分类
     */
//    @Transactional(rollbackFor = Exception.class)
    public Integer refundAndReceiptWriteOff(OldDataWriteOffJobParam param) {

        // 白名单处理
        writeOffWhitelist(param.getCustomerNoList(),true);

        SelectRefundParamDTO refundParam = null;
        Mono<SelectRefundParamDTO> paramDTO = getSelectRefundParamDTO(param);
        refundParam = paramDTO.block();

        AtomicInteger count = new AtomicInteger(0);
        while (true){
            Integer result = null;
            try {
                result = transactionTemplate.execute(status -> getWriteOffResult(refundParam));
            }catch (Exception e){
                log.error("核销异常:{}",refundParam,e);
                if (count.incrementAndGet()>10) {
                    break;
                }
            }
            if(Integer.valueOf(1).equals(result)){
                break;
            }
        }

        return 1;
    }

    private Mono<SelectRefundParamDTO> getSelectRefundParamDTO(OldDataWriteOffJobParam param) {
        SelectRefundParamDTO refundParam = new SelectRefundParamDTO();
        refundParam.setBusinessDateStart(param.getRefundBusinessDateStart());
        refundParam.setBusinessDateEnd(param.getRefundBusinessDateEnd());
        refundParam.setForUpdate(true);
        refundParam.setCustomerNoList(param.getCustomerNoList());
        refundParam.setBillNoList(param.getBillNoList());

        return Mono.just(refundParam);
    }

    private Integer getWriteOffResult(SelectRefundParamDTO refundParam) {
        Page<ReceiveRefundBO> unWriteRefundList = receiveRefundServer.getUnWriteRefundList(new Page<>(0, CommonCons.PAGE_SIZE), refundParam);

        List<ReceiveRefundBO> refundList = unWriteRefundList.getRecords();
        Flux.fromIterable(refundList)
        if (CollectionUtils.isEmpty(refundList)) {
            log.info("正在查询单据退款单数据,没有数据: {}", refundParam);
            return 1;
        }
        Iterator<ReceiveRefundBO> iterator = refundList.iterator();
        while (iterator.hasNext()) {
            ReceiveRefundBO next = iterator.next();
            if (!writeOffWhitelist(next.getCustomerNo())) {
                iterator.remove();
            }
        }
        if (CollectionUtils.isEmpty(refundList)) {
            log.info("正在查询单据退款单数据,没有数据: {}", refundParam);
            return 1;
        }
        List<String> customerList = refundList.stream().map(info -> info.getCustomerNo()).distinct().collect(Collectors.toList());
        ArrayListMultimap<String, ReceiveDocumentBO> receiptMap = receiveDocumentService.unWriteOffReceiptList(customerList);
        if (receiptMap == null || receiptMap.isEmpty()) {
            if(refundList.size() < CommonCons.PAGE_SIZE){
                return 1;
            }
            log.info("没有查询到收款单数据");
            return 2;
        }
        refundParam.setBusinessDateStart(refundList.get(refundList.size() - 1).getBillDate());
        for (ReceiveRefundBO rr : refundList) {
            refundReceiptWriteOff(rr, receiptMap);
        }
        if (refundList.size() < CommonCons.PAGE_SIZE) {
            return 1;
        }
        return 2;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer manualRefundWriteOff(ArWriteOffInsertRequest request) {
        if (!SourceDocumentTypeEnum.REFUND_TYPE.getCode().equals(request.getSourceDocumentType())) {
            log.error("源单据类型必须是退款单类型:{}",request);
            throw new BusinessException("源单据类型必须是退款单类型");
        }

        // 查询退款单数据
        SelectRefundParamDTO paramDTO = new SelectRefundParamDTO();
        paramDTO.setBillNoList(Lists.newArrayList(request.getArReceiveNo()));
        paramDTO.setForUpdate(true);
        List<ReceiveRefundBO> refundList = receiveRefundServer.getUnWriteRefundList(paramDTO);
        if(CollectionUtils.isEmpty(refundList)){
            log.error("当前退款单不能核销:{}",request.getArReceiveNo());
            throw new BusinessException("当前退款单不能核销:"+request.getArReceiveNo());
        }
        if (refundList.size() > 1) {
            log.error("当前退款单数据存在多笔数据:{}",request.getArReceiveNo());
            throw new BusinessException("当前退款单数据存在多笔数据:"+request.getArReceiveNo());
        }

        // 白名单
        refundList = refundList.stream().filter(info -> writeOffWhitelist(info.getCustomerNo())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(refundList)){
            log.info("白名单");
            return 1;
        }

        if (WriteOffTargetTypeEnum.RECEIPT_TYPE.getCode().equals(request.getTargetType())) {
            //收款单

            // 查询收款单数据
            SelectReceiptParamDTO receiptParamDTO = new SelectReceiptParamDTO();
            receiptParamDTO.setBillNoList(Lists.newArrayList(request.getTargetNo()));
            receiptParamDTO.setForUpdate(true);
            ArrayListMultimap<String, ReceiveDocumentBO> receiptMap = receiveDocumentService.unWriteOffReceiptList(receiptParamDTO);

            if(receiptMap == null || receiptMap.isEmpty()){
                log.error("当前收款单数据不存在:{}",request.getTargetNo());
                throw new BusinessException("当前收款单数据不存在:"+request.getTargetNo());
            }
            if (receiptMap.size() > 1) {
                log.error("当前收款单数据存在多笔数据:{}",request.getTargetNo());
                throw new BusinessException("当前收款单数据存在多笔数据:"+request.getTargetNo());
            }
            if (!receiptMap.containsKey(refundList.get(0).getCustomerNo())) {
                log.error("两边客户编码不一致:{}",request);
                throw new BusinessException("两边客户编码不一致");
            }
            // 核销
            for (ReceiveRefundBO rr : refundList) {
                refundReceiptWriteOff(rr,receiptMap);
            }

//            List<ReceiveDocumentBO> collect = receiptMap.entries().stream().map(Map.Entry::getValue)
//                    .filter(info -> Objects.equals(info.getParticipation(), Boolean.TRUE))
//                    .collect(Collectors.toList());
//
//            collect.stream().flatMap(info->Streams.stream(info.getDetailList()))
//                    .forEach(info->{receiveDocumentBiz.updateWriteOff(info);});
//            collect.forEach(info->{receiveDocumentService.updateMoneyByDetail(info.getId());});

        }else{
            // 退货应收单

            // 查询收款单数据
            SelectReturnParamDTO receiptParamDTO = new SelectReturnParamDTO();
            receiptParamDTO.setBillNoList(Lists.newArrayList(request.getTargetNo()));
            receiptParamDTO.setForUpdate(true);
            ArrayListMultimap<String, ReceiveReturnBO> returnMap = arReceiveReturnService.unWriteOffReturnList(receiptParamDTO);
            if(returnMap == null || returnMap.isEmpty()){
                log.error("当前退货应收单数据不存在:{}",request.getTargetNo());
                throw new BusinessException("当前退货应收单数据不存在:"+request.getTargetNo());
            }
            if (returnMap.size() > 1) {
                log.error("当前退货应收单数据存在多笔数据:{}",request.getTargetNo());
                throw new BusinessException("当前退货应收单数据存在多笔数据:"+request.getTargetNo());
            }
            if (!returnMap.containsKey(refundList.get(0).getCustomerNo())) {
                log.error("两边客户编码不一致:{}",request);
                throw new BusinessException("两边客户编码不一致");
            }


            // 核销
            for (ReceiveRefundBO rr : refundList) {
                refundReturnWriteOff(rr,returnMap);
            }

            List<ArReceiveReturn> collect = returnMap.entries().stream().map(Map.Entry::getValue)
                    .filter(info -> Objects.equals(info.getParticipation(), Boolean.TRUE))
                    .map(info->{
                        ArReceiveReturn arReceiveReturn = new ArReceiveReturn();
                        arReceiveReturn.setId(info.getId());
                        arReceiveReturn.setReceiveOffMoney(info.getReceiveOffMoney());
                        arReceiveReturn.setReceiveUselessMoney(info.getReceiveUselessMoney());
                        arReceiveReturn.setWriteOffStatus(info.getWriteOffStatus());
                        return arReceiveReturn;
                    })
                    .collect(Collectors.toList());
            if(!CollectionUtils.isEmpty(collect)){
                collect.forEach(arReceiveReturnService::updateArReceiveReturn);
            }
        }



        return 1;
    }

    /**
     * 反核销
     * @param arWriteOff
     * @return
     */
    @Override
    public Integer doRefundCancelWriteOff(ArWriteOff arWriteOff) {

        if(BigDecimalUtil.compareTo(arWriteOff.getWriteOffMoney(),BigDecimal.ZERO) <=0){
            log.warn("核销金额为0 不反核销");
            return 0;
        }

        // 修改详情
        updateDetail(arWriteOff);

        // 修改主表
        updateMain(arWriteOff);

        if (WriteOffTargetTypeEnum.RECEIPT_TYPE.getCode().equals(arWriteOff.getTargetType())) {
            receiptDocumentServer.doWriteOffCancel(arWriteOff);
        }else{
            returnDocumentServer.doWriteOffCancel(arWriteOff);
        }

        return 1;
    }

    private void updateDetail(ArWriteOff arWriteOff) {
        ReceiveRefundDetail receiveRefundDetail = receiveRefundDetailMapper.selectById(arWriteOff.getSourceItemNo());
        if (Objects.isNull(receiveRefundDetail)) {
            log.info("退款单详情不存在:{}",arWriteOff.getSourceItemNo());
            throw new BusinessException("退款单详情不存在:"+arWriteOff.getSourceItemNo());
        }
        // 反核销： 核销金额减少
        BigDecimal offMoney= BigDecimalUtil.subtract(receiveRefundDetail.getReceiveOffMoney(), arWriteOff.getWriteOffMoney());
        // 未核销金额
        BigDecimal unOffMoney = BigDecimalUtil.subtract(receiveRefundDetail.getShouldAmount(), offMoney);
        receiveRefundDetail.setReceiveOffMoney(offMoney);
        receiveRefundDetail.setReceiveUselessMoney(unOffMoney);
        receiveRefundDetail.setWriteOffStatus(WriteOffStatusEnum.getCode(offMoney,unOffMoney));
        receiveRefundDetailMapper.updateReceiveRefundDetail(receiveRefundDetail);
        log.info("退款单详情反核销完毕,反核销金额:{},编码{}",arWriteOff.getWriteOffMoney(),arWriteOff.getSourceItemNo());
    }

    private void updateMain(ArWriteOff arWriteOff) {

        // 修改主表
        ReceiveRefund receiveRefund = receiveRefundMapper.getReceiveRefundByBillNo(arWriteOff.getSourceNo());
        if(Objects.isNull(receiveRefund)){
            log.info("退款单不存在:{}",arWriteOff.getSourceNo());
            throw new BusinessException("退款单不存在:"+arWriteOff.getSourceNo());
        }
        if(BigDecimalUtil.compareTo(receiveRefund.getReceiveOffMoney(),arWriteOff.getWriteOffMoney()) < 0){
            log.warn("退款单[{}]已核销金额:{}小于反核销金额:{}，不允许反核销",
                    receiveRefund.getBillNo(),receiveRefund.getReceiveOffMoney(),arWriteOff.getWriteOffMoney());
            throw new ImsException("退款单已核销金额小于反核销金额，不允许反核销");
        }

        // 反核销： 核销金额减少
        BigDecimal offMoney= BigDecimalUtil.subtract(receiveRefund.getReceiveOffMoney(), arWriteOff.getWriteOffMoney());
        // 未核销金额
        BigDecimal unOffMoney = BigDecimalUtil.subtract(receiveRefund.getShouldAmount(), offMoney);
        ReceiveRefundBO refundBO = new ReceiveRefundBO();
        refundBO.setReceiveOffMoney(offMoney);
        refundBO.setReceiveUselessMoney(unOffMoney);
        refundBO.setWriteOffStatus(WriteOffStatusEnum.getCode(offMoney,unOffMoney));
        refundBO.setId(receiveRefund.getId());

        receiveRefundMapper.updateReceiveRefund(refundBO);
        log.info("退款单记录反核销完毕,反核销金额:{},编码{}",arWriteOff.getWriteOffMoney(),arWriteOff.getSourceNo());
    }


    /**
     * 收款单的核销
     * @param rr
     * @param receiptMap
     */
    public BigDecimal refundReceiptWriteOff(ReceiveRefundBO rr, ArrayListMultimap<String, ReceiveDocumentBO> receiptMap) {

        List<ReceiveRefundDetail> detailList = rr.getDetailList();

        refundCheckAmount(detailList, rr);

        // 前置条件 todo cliff
        List<ReceiveDocumentBO> receiptList = receiptMap.get(rr.getCustomerNo());
        if(CollectionUtils.isEmpty(receiptList)){
            return BigDecimal.ZERO;
        }
        // 匹配条件 todo cliff
        BigDecimal reduce = getReceiptUnWriteOffAmount(receiptList);

        BigDecimal writeOffMoney = BigDecimal.ZERO;

        // 核销记录
        for (ReceiveRefundDetail detail : detailList) {
            // 前置判断
            if (BigDecimalUtil.compareTo(detail.getReceiveUselessMoney(), BigDecimal.ZERO) <= 0) {
                log.info("退款单的未核销金额为0跳过:[{}]", detail.getRefundId());
                continue;
            }
            BigDecimal currentWriteOffMoney = BigDecimalUtil.min(detail.getReceiveUselessMoney(), reduce);
            writeOffMoney = BigDecimalUtil.add(writeOffMoney, currentWriteOffMoney);
            // 核销： 未核销金额减少
            BigDecimal receiptUnOffMoney = BigDecimalUtil.subtract(detail.getReceiveUselessMoney(), currentWriteOffMoney);
            // 单据新的核销金额
            BigDecimal receiptOffMoney = BigDecimalUtil.subtract(detail.getShouldAmount(), receiptUnOffMoney);

            detail.setReceiveOffMoney(receiptOffMoney);
            detail.setReceiveUselessMoney(receiptUnOffMoney);
            detail.setWriteOffStatus(WriteOffStatusEnum.getCode(receiptOffMoney, receiptUnOffMoney));

            //
            ArWriteOff arWriteOff = buildArWriteOff(rr, String.valueOf(detail.getId()));
            doRefundToReceiptHandler(receiptList,currentWriteOffMoney,arWriteOff);

            // 修改数据库退款单详情数据
            receiveRefundDetailMapper.updateReceiveRefundDetail(detail);

        }

        //
        rr.setReceiveUselessMoney(BigDecimalUtil.subtract(rr.getReceiveUselessMoney(), writeOffMoney));
        rr.setReceiveOffMoney(BigDecimalUtil.subtract(rr.getShouldAmount(), rr.getReceiveUselessMoney()));
        rr.setWriteOffStatus(WriteOffStatusEnum.getCode(rr.getReceiveOffMoney(), rr.getReceiveUselessMoney()));

        // 修改数据库的退款单数据
        receiveRefundMapper.updateReceiveRefund(rr);

        return writeOffMoney;
    }


    @Transactional(rollbackFor = Exception.class)
    public BigDecimal refundReturnWriteOff(ReceiveRefundBO rr, ArrayListMultimap<String, ReceiveReturnBO> receiptMap) {

        List<ReceiveRefundDetail> detailList = rr.getDetailList();

        refundCheckAmount(detailList, rr);

        // 前置条件 todo cliff
        List<ReceiveReturnBO> returnList = receiptMap.get(rr.getCustomerNo());
        // 匹配条件 todo cliff
        BigDecimal reduce = getReturnUnWriteOffAmount(returnList);

        BigDecimal writeOffMoney = BigDecimal.ZERO;

        // 核销记录


        for (ReceiveRefundDetail detail : detailList) {
            // 前置判断
            if (BigDecimalUtil.compareTo(detail.getReceiveUselessMoney(), BigDecimal.ZERO) <= 0) {
                log.info("退款单的未核销金额为0跳过:[{}]", detail.getRefundId());
                continue;
            }
            // 计算核销金额
            BigDecimal currentWriteOffMoney = BigDecimalUtil.min(detail.getReceiveUselessMoney(), reduce);
            writeOffMoney = BigDecimalUtil.add(writeOffMoney, currentWriteOffMoney);
            // 核销： 未核销金额减少
            BigDecimal receiptUnOffMoney = BigDecimalUtil.subtract(detail.getReceiveUselessMoney(), currentWriteOffMoney);
            // 单据新的核销金额
            BigDecimal receiptOffMoney = BigDecimalUtil.subtract(detail.getShouldAmount(), receiptUnOffMoney);

            detail.setReceiveOffMoney(receiptOffMoney);
            detail.setReceiveUselessMoney(receiptUnOffMoney);
            detail.setWriteOffStatus(WriteOffStatusEnum.getCode(receiptOffMoney, receiptUnOffMoney));

            //
            ArWriteOff arWriteOff = buildArWriteOff(rr, String.valueOf(detail.getId()));
            doRefundToReturnHandler(returnList,currentWriteOffMoney,arWriteOff);


            // 修改数据库退款单详情数据
            receiveRefundDetailMapper.updateReceiveRefundDetail(detail);

        }

        //
        rr.setReceiveUselessMoney(BigDecimalUtil.subtract(rr.getReceiveUselessMoney(), writeOffMoney));
        rr.setReceiveOffMoney(BigDecimalUtil.subtract(rr.getShouldAmount(), rr.getReceiveUselessMoney()));
        rr.setWriteOffStatus(WriteOffStatusEnum.getCode(rr.getReceiveOffMoney(), rr.getReceiveUselessMoney()));

        // 修改数据库的退款单数据
        receiveRefundMapper.updateReceiveRefund(rr);

        return writeOffMoney;
    }


    public void doRefundToReceiptHandler(List<ReceiveDocumentBO> receiptList, BigDecimal writeOffMoney, ArWriteOff arWriteOff) {


        // 判断金额

        for (ReceiveDocumentBO receiptBO : receiptList) {
            if (BigDecimalUtil.compareTo(writeOffMoney, BigDecimal.ZERO) <= 0) {
                break;
            }
            if (BigDecimalUtil.compareTo(receiptBO.getReceiveUselessMoney(), BigDecimal.ZERO) <= 0) {
                log.info("收款单的未核销金额为0跳过:[{}]", receiptBO.getReceiveDocumentNo());
                continue;
            }
            List<ReceiveDocumentDetail> receiptDetailList = receiptBO.getDetailList();
            BigDecimal receiptWriteOffMoney = BigDecimal.ZERO;
            List<ArWriteOffDetail> offDetailList = new ArrayList<>(receiptDetailList.size());

            for (ReceiveDocumentDetail detail : receiptDetailList) {
                if (BigDecimalUtil.compareTo(writeOffMoney, BigDecimal.ZERO) <= 0) {
                    break;
                }
                if (BigDecimalUtil.compareTo(detail.getReceiveUselessMoney(), BigDecimal.ZERO) <= 0) {
                    log.info("收款单详情的未核销金额为0跳过:[{}]", detail.getReceiveDocumentId());
                    continue;
                }
                // 本次需要核销的金额 小于等于 未核销金额
                BigDecimal detailWriteOffMoney = BigDecimalUtil.min(detail.getReceiveUselessMoney(), writeOffMoney);
                receiptWriteOffMoney = BigDecimalUtil.add(receiptWriteOffMoney,detailWriteOffMoney);
                writeOffMoney = BigDecimalUtil.subtract(writeOffMoney, detailWriteOffMoney);
                // 核销： 未核销金额减少
                BigDecimal unOffMoney = BigDecimalUtil.subtract(detail.getReceiveUselessMoney(), detailWriteOffMoney);
                // 单据新的核销金额
                BigDecimal offMoney = BigDecimalUtil.subtract(detail.getReceiveAmount(), unOffMoney);

                detail.setReceiveOffMoney(offMoney);
                detail.setReceiveUselessMoney(unOffMoney);
                detail.setWriteOffStatus(WriteOffStatusEnum.getCode(offMoney, unOffMoney));

                // 详情
                ArWriteOffDetail offDetail = buildArWriteOffDetail(detail,detailWriteOffMoney);
                offDetailList.add(offDetail);
//                // 修改数据库详情数据
                receiveDocumentBiz.updateWriteOff(detail);
            }

            // 修改金额
            receiptBO.setReceiveUselessMoney(BigDecimalUtil.subtract(receiptBO.getReceiveUselessMoney(), receiptWriteOffMoney));
            receiptBO.setReceiveOffMoney(BigDecimalUtil.subtract(receiptBO.getReceiveAmount(), receiptBO.getReceiveUselessMoney()));
            receiptBO.setWriteOffStatus(WriteOffStatusEnum.getCode(receiptBO.getReceiveOffMoney(), receiptBO.getReceiveUselessMoney()));
            receiptBO.setParticipation(true);

            // 收款单
            receiveDocumentService.updateMoneyByDetail(receiptBO.getId());

            // 记录核销记录
            arWriteOff.setArWriteOffNo(SerialNo.getArOffWrite());
            arWriteOff.setTargetNo(receiptBO.getReceiveDocumentNo());
            arWriteOff.setTargetAmount(receiptBO.getReceiveAmount());
            arWriteOff.setTargetType(WriteOffTargetTypeEnum.RECEIPT_TYPE.getCode());
            arWriteOff.setWriteOffMoney(receiptWriteOffMoney);
            arWriteOff.setArWriteOffType(Integer.valueOf(receiptBO.getReceiveUse()));
            arWriteOff.setRemark(arWriteOff.getRemark()+receiptBO.getReceiveDocumentNo());

            arWriteOffServer.insert(arWriteOff);

            List<ArWriteOffDetail> collect = offDetailList.stream().map(info -> {
                info.setArWriteOffId(arWriteOff.getId());
                return info;
            }).collect(Collectors.toList());

            arWriteOffDetailServer.saveBatch(collect);

        }
    }

    public void doRefundToReturnHandler(List<ReceiveReturnBO> returnList, BigDecimal writeOffMoney, ArWriteOff arWriteOff) {


        // 判断金额
        BigDecimal receiptWriteOffMoney = BigDecimal.ZERO;

        for (ReceiveReturnBO returnBO : returnList) {
            if (BigDecimalUtil.compareTo(writeOffMoney, BigDecimal.ZERO) <= 0) {
                log.info("退款单核销退货应收单，核销金额为零");
                break;
            }
            if (BigDecimalUtil.compareTo(returnBO.getReceiveUselessMoney(), BigDecimal.ZERO) <= 0) {
                log.info("退货应收单的未核销金额为0跳过:[{}]", returnBO.getArReceiveReturnNo());
                continue;
            }

            // 本次需要核销的金额 小于等于 未核销金额
            BigDecimal detailWriteOffMoney = BigDecimalUtil.min(returnBO.getReceiveUselessMoney(), writeOffMoney);
            receiptWriteOffMoney = BigDecimalUtil.add(receiptWriteOffMoney,detailWriteOffMoney);
            writeOffMoney = BigDecimalUtil.subtract(writeOffMoney, detailWriteOffMoney);


            // 修改金额
            returnBO.setReceiveUselessMoney(BigDecimalUtil.subtract(returnBO.getReceiveUselessMoney(), detailWriteOffMoney));
            returnBO.setReceiveOffMoney(BigDecimalUtil.subtract(returnBO.getAmount().abs(), returnBO.getReceiveUselessMoney()));
            returnBO.setWriteOffStatus(WriteOffStatusEnum.getCode(returnBO.getReceiveOffMoney(), returnBO.getReceiveUselessMoney()));
            returnBO.setParticipation(true);

            // 记录核销记录
            arWriteOff.setArWriteOffNo(SerialNo.getArOffWrite());
            arWriteOff.setWriteOffMoney(detailWriteOffMoney);
            arWriteOff.setArWriteOffType(Integer.valueOf(WriteOffTypeEnum.BALANCE.getCode()));
            arWriteOff.setTargetNo(returnBO.getArReceiveReturnNo());
            arWriteOff.setTargetAmount(returnBO.getAmount());
            arWriteOff.setTargetType(WriteOffTargetTypeEnum.RECEIVE_RETURN_TYPE.getCode());
            arWriteOff.setRemark(arWriteOff.getRemark()+returnBO.getArReceiveReturnNo());
            arWriteOffServer.insert(arWriteOff);

        }

        // 修改 todo cliff

//        arReceiveReturnService.updateArReceiveReturn();


    }

    private ArWriteOffDetail buildArWriteOffDetail(ReceiveDocumentDetail detail,BigDecimal detailWriteOffMoney) {
        ArWriteOffDetail offDetail = new ArWriteOffDetail();
        offDetail.setReceiveDocumentDetailId(detail.getId())
                .setWriteOffMoney(detailWriteOffMoney)
                .setReceiveUse(detail.getReceiveUse())
                .setOperatorSource(OperatorSourceEnum.MANUAL_TYPE.getCode());
        return offDetail;
    }


    private BigDecimal getReceiptUnWriteOffAmount(List<ReceiveDocumentBO> receiptList) {
        Optional<BigDecimal> reduce = receiptList.stream().map(ReceiveDocumentBO::getReceiveUselessMoney).filter(Objects::nonNull).reduce(BigDecimal::add);
        reduce.orElse(BigDecimal.ZERO);
        return reduce.get();
    }
    private BigDecimal getReturnUnWriteOffAmount(List<ReceiveReturnBO> receiptList) {
        return receiptList.stream().map(ReceiveReturnBO::getReceiveUselessMoney).filter(Objects::nonNull).reduce(BigDecimal::add).get();
    }


    /**
     * 收款单的核销
     *
     * @param rd
     * @param refundMap
     */
    public BigDecimal receiptDocumentWriteOff(ReceiveDocumentBO rd, ArrayListMultimap<String, ReceiveRefundBO> refundMap) {

        List<ReceiveDocumentDetail> documentDetailList = rd.getDetailList();

        checkAmount(documentDetailList, rd);

        // 前置条件 todo cliff
        List<ReceiveRefundBO> refundList = refundMap.get(rd.getCustomerNo());
        // 匹配条件 todo cliff
        BigDecimal reduce = refundList.stream().map(ReceiveRefundBO::getReceiveUselessMoney).filter(Objects::nonNull).reduce(BigDecimal::add).get();

        BigDecimal writeOffMoney = BigDecimal.ZERO;
        for (ReceiveDocumentDetail receiveDocumentDetail : documentDetailList) {
            // 前置判断
            if(BigDecimalUtil.compareTo(writeOffMoney,BigDecimal.ZERO)<=0){
                log.info("收款单中核销金额小于等于0不能继续核销");
                break;
            }
            if (BigDecimalUtil.compareTo(receiveDocumentDetail.getReceiveUselessMoney(), BigDecimal.ZERO) <= 0) {
                log.info("收款单的未核销金额为0跳过:[{}]", receiveDocumentDetail.getReceiveDocumentId());
                continue;
            }
            BigDecimal min = BigDecimalUtil.min(receiveDocumentDetail.getReceiveUselessMoney(), reduce);
            writeOffMoney = BigDecimalUtil.add(writeOffMoney, min);
            // 核销： 未核销金额减少
            BigDecimal receiptUnOffMoney = BigDecimalUtil.subtract(receiveDocumentDetail.getReceiveUselessMoney(), min);
            // 单据新的核销金额
            BigDecimal receiptOffMoney = BigDecimalUtil.subtract(receiveDocumentDetail.getReceiveAmount(), receiptUnOffMoney);

            receiveDocumentDetail.setReceiveOffMoney(receiptOffMoney);
            receiveDocumentDetail.setReceiveUselessMoney(receiptUnOffMoney);
            receiveDocumentDetail.setWriteOffStatus(WriteOffStatusEnum.getCode(receiptOffMoney, receiptUnOffMoney));

        }

        //
        rd.setReceiveUselessMoney(BigDecimalUtil.subtract(rd.getReceiveUselessMoney(), writeOffMoney));
        rd.setReceiveOffMoney(BigDecimalUtil.subtract(rd.getReceiveAmount(), rd.getReceiveUselessMoney()));
        rd.setWriteOffStatus(WriteOffStatusEnum.getCode(rd.getReceiveOffMoney(), rd.getReceiveUselessMoney()));

        // 修改数据库的数据




        return writeOffMoney;
    }

    /**
     * 检测金额
     * @param detailList
     * @param rr
     */
    private void refundCheckAmount(List<ReceiveRefundDetail> detailList, ReceiveRefundBO rr) {
        BigDecimal receiveAmount = BigDecimal.ZERO;
        BigDecimal receiveUselessMoney = BigDecimal.ZERO;
        BigDecimal receiveOffMoney = BigDecimal.ZERO;
        for (ReceiveRefundDetail rdd : detailList) {
            receiveAmount = BigDecimalUtil.add(receiveAmount, rdd.getShouldAmount());
            receiveUselessMoney = BigDecimalUtil.add(receiveUselessMoney, rdd.getReceiveUselessMoney());
            receiveOffMoney = BigDecimalUtil.add(receiveOffMoney, rdd.getReceiveOffMoney());
        }
        if (BigDecimalUtil.compareTo(rr.getShouldAmount(), receiveAmount) != 0 ||
                BigDecimalUtil.compareTo(rr.getReceiveUselessMoney(), receiveUselessMoney) != 0 ||
                BigDecimalUtil.compareTo(rr.getReceiveOffMoney(), receiveOffMoney) != 0) {
            log.error("退款单的金额与详情的金额不一致:{}",rr.getBillNo());
            throw new BusinessException("退款单的金额与详情的金额不一致:"+rr.getBillNo());
        }
    }

    private void checkAmount(List<ReceiveDocumentDetail> documentDetailList, ReceiveDocumentBO rd) {
        BigDecimal receiveAmount = BigDecimal.ZERO;
        BigDecimal receiveUselessMoney = BigDecimal.ZERO;
        BigDecimal receiveOffMoney = BigDecimal.ZERO;
        for (ReceiveDocumentDetail rdd : documentDetailList) {
            receiveAmount = BigDecimalUtil.add(receiveAmount, rdd.getReceiveAmount());
            receiveUselessMoney = BigDecimalUtil.add(receiveUselessMoney, rdd.getReceiveUselessMoney());
            receiveOffMoney = BigDecimalUtil.add(receiveOffMoney, rdd.getReceiveOffMoney());
        }
        if (BigDecimalUtil.compareTo(rd.getReceiveAmount(), receiveAmount) != 0 ||
                BigDecimalUtil.compareTo(rd.getReceiveUselessMoney(), receiveUselessMoney) != 0 ||
                BigDecimalUtil.compareTo(rd.getReceiveOffMoney(), receiveOffMoney) != 0) {
            log.error("收款单的金额与详情的金额不一致:{}",rd.getReceiveDocumentNo());
            throw new BusinessException("收款单的金额与详情的金额不一致:"+rd.getReceiveDocumentNo());
        }
    }

    public void dd(List<ReceiveRefundBO> refundList, BigDecimal writeOffMoney) {


        for (ReceiveRefundBO refundBO : refundList) {
            if (BigDecimalUtil.compareTo(writeOffMoney, BigDecimal.ZERO) <= 0) {
                break;
            }
            if (BigDecimalUtil.compareTo(refundBO.getReceiveUselessMoney(), BigDecimal.ZERO) <= 0) {
                log.info("退款单的未核销金额为0跳过:[{}]", refundBO.getBillNo());
                continue;
            }
            List<ReceiveRefundDetail> refundDetailList = refundBO.getDetailList();

            for (ReceiveRefundDetail receiveRefundDetail : refundDetailList) {
                if (BigDecimalUtil.compareTo(writeOffMoney, BigDecimal.ZERO) <= 0) {
                    break;
                }
                if (BigDecimalUtil.compareTo(receiveRefundDetail.getReceiveUselessMoney(), BigDecimal.ZERO) <= 0) {
                    log.info("退款单详情的未核销金额为0跳过:[{}]", receiveRefundDetail.getRefundId());
                    continue;
                }
                // 本次需要核销的金额 小于等于 未核销金额
                BigDecimal detailWriteOffMoney = BigDecimalUtil.min(receiveRefundDetail.getReceiveUselessMoney(), writeOffMoney);
                writeOffMoney = BigDecimalUtil.subtract(writeOffMoney, detailWriteOffMoney);
                // 核销： 未核销金额减少
                BigDecimal unOffMoney = BigDecimalUtil.subtract(receiveRefundDetail.getReceiveUselessMoney(), detailWriteOffMoney);
                // 单据新的核销金额
                BigDecimal offMoney = BigDecimalUtil.subtract(receiveRefundDetail.getShouldAmount(), unOffMoney);

                receiveRefundDetail.setReceiveOffMoney(offMoney);
                receiveRefundDetail.setReceiveUselessMoney(unOffMoney);
                receiveRefundDetail.setWriteOffStatus(WriteOffStatusEnum.getCode(offMoney, unOffMoney));
                refundBO.setReceiveOffMoney(offMoney);
                refundBO.setReceiveUselessMoney(unOffMoney);
                refundBO.setWriteOffStatus(WriteOffStatusEnum.getCode(offMoney, unOffMoney));

            }

        }
    }




    public void ee() {

        // 白名单处理


        ArrayListMultimap<String, ReceiveDocumentBO> receiptMap = ArrayListMultimap.create();
        ArrayListMultimap<String, ReceiveRefundBO> refundMap = ArrayListMultimap.create();

        List<ReceiveDocumentBO> receiptList = receiptMap.get("客户编码");


        for (ReceiveDocumentBO rd : receiptList) {

            List<ReceiveDocumentDetail> documentDetailList = rd.getDetailList();
            for (ReceiveDocumentDetail receiveDocumentDetail : documentDetailList) {

                // 前置条件

                List<ReceiveRefundBO> refundList = refundMap.get(rd.getCustomerNo());
                // 匹配条件

                BigDecimal reduce = refundList.stream().map(ReceiveRefundBO::getReceiveUselessMoney).filter(Objects::nonNull).reduce(BigDecimal::add).get();

                BigDecimal min = BigDecimalUtil.min(receiveDocumentDetail.getReceiveUselessMoney(), reduce);
                BigDecimal writeOffMoney = min;
                // 核销： 未核销金额减少
                BigDecimal receiptUnOffMoney = BigDecimalUtil.subtract(receiveDocumentDetail.getReceiveUselessMoney(), min);
                // 单据新的核销金额
                BigDecimal receiptOffMoney = BigDecimalUtil.subtract(receiveDocumentDetail.getReceiveAmount(), receiptUnOffMoney);

                receiveDocumentDetail.setReceiveOffMoney(receiptOffMoney);
                receiveDocumentDetail.setReceiveUselessMoney(receiptUnOffMoney);
                receiveDocumentDetail.setWriteOffStatus(WriteOffStatusEnum.getCode(receiptOffMoney, receiptUnOffMoney));


                for (ReceiveRefundBO refundBO : refundList) {
                    if (BigDecimalUtil.compareTo(writeOffMoney, BigDecimal.ZERO) <= 0) {
                        break;
                    }
                    if (BigDecimalUtil.compareTo(refundBO.getReceiveUselessMoney(), BigDecimal.ZERO) <= 0) {
                        log.info("退款单的未核销金额为0跳过:[{}]", refundBO.getBillNo());
                        continue;
                    }
                    List<ReceiveRefundDetail> refundDetailList = refundBO.getDetailList();

                    for (ReceiveRefundDetail receiveRefundDetail : refundDetailList) {
                        if (BigDecimalUtil.compareTo(writeOffMoney, BigDecimal.ZERO) <= 0) {
                            break;
                        }
                        if (BigDecimalUtil.compareTo(receiveRefundDetail.getReceiveUselessMoney(), BigDecimal.ZERO) <= 0) {
                            log.info("退款单详情的未核销金额为0跳过:[{}]", receiveRefundDetail.getRefundId());
                            continue;
                        }
                        // 本次需要核销的金额 小于等于 未核销金额
                        BigDecimal detailWriteOffMoney = BigDecimalUtil.min(receiveRefundDetail.getReceiveUselessMoney(), writeOffMoney);
                        writeOffMoney = BigDecimalUtil.subtract(writeOffMoney, detailWriteOffMoney);
                        // 核销： 未核销金额减少
                        BigDecimal unOffMoney = BigDecimalUtil.subtract(receiveRefundDetail.getReceiveUselessMoney(), detailWriteOffMoney);
                        // 单据新的核销金额
                        BigDecimal offMoney = BigDecimalUtil.subtract(receiveRefundDetail.getShouldAmount(), unOffMoney);

                        receiveRefundDetail.setReceiveOffMoney(offMoney);
                        receiveRefundDetail.setReceiveUselessMoney(unOffMoney);
                        receiveRefundDetail.setWriteOffStatus(WriteOffStatusEnum.getCode(offMoney, unOffMoney));
                        refundBO.setReceiveOffMoney(offMoney);
                        refundBO.setReceiveUselessMoney(unOffMoney);
                        refundBO.setWriteOffStatus(WriteOffStatusEnum.getCode(offMoney, unOffMoney));

                    }

                }
            }
        }

    }

    private ArWriteOff buildArWriteOff(ReceiveRefundBO rr,String sourceItemNo){

        return  ArWriteOff.builder()
                .sourceDocumentType(SourceDocumentTypeEnum.REFUND_TYPE.getCode())
                .sourceNo(rr.getBillNo())
                .sourceItemNo(sourceItemNo)
                .writeOffMoney(BigDecimal.ZERO)
                .orgCode(rr.getOrgCode())
                .orgName(rr.getOrgName())
                .roleId(rr.getRoleId())
                .customerName(rr.getCustomerName())
                .customerNo(rr.getCustomerNo())
                .saleUserId(rr.getSaleUserId())
                .saleUserName(rr.getSaleUserName())
                .saleUserId(rr.getSaleUserId())
                .operatorSource(2)
                .remark(rr.getBillNo()+"_")
                .createUserId(ReqCtxUtil.getUid())
                .createUserName(ReqCtxUtil.getFlowerName())
                .dataStatus(WriteOffDataStatusEnum.NORMAL_DATA_STATUS.getCode())
                .build();



//        if (Objects.equals(writeOffPlanBean.getIsSystem().getCode(), IsSystemEnum.SYSTEM_STATUS.getCode()) ||
//                Objects.equals(writeOffPlanBean.getIsSystem().getCode(), IsSystemEnum.OLD_DATA_STATUS.getCode())) {
//            entity.setOperatorSource(1);
//            entity.setCreateUserId("0");
//            entity.setCreateUserName(WriteOffCommon.getSystemOperator());
//        }
//

    }


}
